package mote;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * MoteSimulator simulates a mote to make it easier to test and program contains a list of all motes currently connected to MoteBridge
 * @author Khalid Alhokail, Vineet Gupta, Oren Melzer, David Schneider
 *
 */
public class MoteSimulator extends JFrame implements MoteInterface{
	private static final long serialVersionUID = 10000;
	private ArrayList<Mote> motes;
	private int minValue = 30; // Minimum mote reading possible
	private int maxValue = 3000; // Maximum mote reading possible
	private int textColWidth = 4;
	private ArrayList<JLabel> lblMotes = new ArrayList<JLabel>();
	private ArrayList<JTextField> readings = new ArrayList<JTextField>();
	private ArrayList<JCheckBox> checkBoxes = new ArrayList<JCheckBox>();
	private ArrayList<JSlider> sliders = new ArrayList<JSlider>();
	private JButton btnchangeMode;
	private int currentY;
	private int keypress[];
	private java.util.Timer timer;
	private boolean timerIsEnabled;
	private GridBagConstraints constraints;
	private Random random;
	
	/**
	 * MoteSimulator can run stand alone, or can be called by some other 
	 * module. For the testing purpose there is main method and it gets 
	 * invoked when this simulator it run stand alone.
	 * 
	 * Simulator can run in two modes. In Auto mode, based on lowest and
	 * highest reading criteria, its either put in light or in dark (by 
	 * selecting or not selecting Light check box. Now based on mote id,
	 * we can periodically push keyboard keys 1, 2, 3 etc, to change its
	 * reading.
	 * 
	 * In manual mode, reading is controlled by slider adjacent to every
	 * mote.
	 */
	public MoteSimulator() {
		setResizable(false);
		motes = new ArrayList<Mote>();
		
	    random = new Random();
	    
	    enableTimer(); // We start with auto mode
		
		keypress = new int[10];
        
        // Center window in middle of screen
        Toolkit toolkit = getToolkit();
        Dimension size = toolkit.getScreenSize();
        setLocation(size.width/2-getWidth()/2, size.height/2-getHeight());
        
        // Set the display layout
        setLayout(new GridBagLayout());
        constraints = new GridBagConstraints();   
     
        // Banner
        JLabel lblBanner = new JLabel("Mote Simulator"); // Set text
        lblBanner.setFont(lblBanner.getFont().deriveFont(18.0f)); // Increase font
        lblBanner.addKeyListener(new MyKeyListener()); // Make it listen to key press
        constraints.insets = new Insets(10, 0, 10, 0); // Spacing around it
        constraints.gridwidth = 4; // Spanning over 3 columns for this layout
        add(lblBanner, constraints); // Add banner to current frame
        
        // Button to add new mote 
        JButton btnAddMote = new JButton("Add Mote");
        constraints.insets = new Insets(5, 10, 5, 10); // Spacing around it
        constraints.gridwidth = 1; // Spanning over 1 columns
        btnAddMote.addKeyListener(new MyKeyListener()); // Make it listen to key press
        btnAddMote.setMnemonic(KeyEvent.VK_A); // ALT + A shortcut
        constraints.gridy = 1;
        add(btnAddMote, constraints);
        
        // This will enable button to add new motes
        btnAddMote.addActionListener(new ActionListener() {
        	public void actionPerformed(ActionEvent event) {
        		motes.add(new Mote(lblMotes.size()));
        		addMoteRow();
        	}
        });
        
        // Button to change mode (auto mode and manual mode)
        btnchangeMode = new JButton("Set Manual");
        constraints.insets = new Insets(5, 10, 5, 10); // Spacing around it
        constraints.gridwidth = 2; // Spanning over 1 columns
        btnchangeMode.addKeyListener(new MyKeyListener()); // Make it listen to key press
        btnchangeMode.setMnemonic(KeyEvent.VK_S); // ALT + A shortcut
        constraints.gridy = 1;
        add(btnchangeMode, constraints);
        
        currentY = 1;
  
        btnchangeMode.addActionListener(new ActionListener() {
        	public void actionPerformed(ActionEvent event) {
        		if (timerIsEnabled) {
        			btnchangeMode.setText("Set Auto");
        			disableTimer();
        		} else {
        			btnchangeMode.setText("Set Manual");
        			enableTimer();
        		}
        	}
        });
        
        // Button to exit application
        JButton btnExit = new JButton("Exit");
        btnExit.setForeground(Color.RED);
        btnExit.setMnemonic(KeyEvent.VK_E);
        constraints.gridwidth = 1;
        btnExit.addKeyListener(new MyKeyListener());
        constraints.gridx = 3;
        //constraints.anchor = GridBagConstraints.WEST;
        add(btnExit, constraints);
        
        btnExit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                System.exit(0);
           }	
        });
        
        // Make application visible
        setTitle("LightMote Simulator");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
        setVisible(true);
	}
    
	/**
	 * Adds a row corresponding to each mote in UI. This also adds
	 * Light check box and slider associated with that mote to the
	 * row in UI.
	 */
	private void addMoteRow() {
		constraints.gridwidth = 1;
		lblMotes.add(new JLabel("Mote "+ Integer.toString(lblMotes.size()+1)));
		
		JLabel temp = lblMotes.get(lblMotes.size()-1);
		constraints.gridx = 0;
		constraints.insets = new Insets(5, 5, 5, 5);
		currentY++;
		
		constraints.gridy = currentY;
		add(temp,constraints);
		
		readings.add(new JTextField("0", textColWidth));
		JTextField txtTemp = readings.get(readings.size()-1);
		
		constraints.gridx = 1;
		
		add(txtTemp, constraints);
		
		checkBoxes.add(new JCheckBox("Light"));
		JCheckBox chkTemp = checkBoxes.get(checkBoxes.size()-1);
		constraints.gridx = 2;
		chkTemp.addKeyListener(new MyKeyListener());
		add(chkTemp,constraints);
		
		sliders.add(new JSlider());
		final JSlider sldTemp = sliders.get(sliders.size()-1);
		sldTemp.setMaximum(maxValue);
		sldTemp.setMajorTickSpacing(100);
		
		final int index = sliders.size()-1;
		
		sldTemp.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				int temp = sldTemp.getValue();
				readings.get(index).setText(String.valueOf(temp));
			}
		});
		
		constraints.gridx = 3;
		sldTemp.addKeyListener(new MyKeyListener());
		add(sldTemp,constraints);
		
		pack();
	}
	
	public boolean setLEDs(int moteId, byte values){
		return false;
	}
	
	public boolean setSamplingInterval(int moteId, int twentiethsOfASecond){
		return false;
	}

    public static void main(String[] args) {
    	MoteSimulator simple = new MoteSimulator();
    	simple.addMote(0);
    	simple.addMote(1);
    } 
    
    /**
     * This will listen to key events when we are in auto mode.
     * This will make sure that each mote with corresponding id
     * to the keys gets updated.
     */
    public class MyKeyListener extends KeyAdapter{
    	public void keyPressed(KeyEvent ke){
    	  
			char i = ke.getKeyChar();
			int moteReading;
			
			if (i >= '1' && i <= '9') {
				int j = Integer.parseInt(Character.toString(i));
				
				if (j > 0 && j <= motes.size()) {
					keypress[j-1]++;
						  
					if (checkBoxes.get(j-1).isSelected()) {
						moteReading = minValue+random.nextInt(300);
						readings.get(j-1).setText(String.valueOf(moteReading));
					}
					else {
						moteReading = maxValue - random.nextInt(300);
						readings.get(j-1).setText(String.valueOf(moteReading));
					}
					
					motes.get(j-1).SetReading(moteReading);
				}
				
			}
        }
    }  
    
    /**
     * Timer will be enabled in auto mode but disabled in manual mode.
     */
    private void enableTimer() {
    	timerIsEnabled = true;
	    
	    timer = new java.util.Timer();
	    
		int delay = 1000;   // delay for 5 sec.
	    int period = 1000;  // repeat every sec.
	    
	    for (int z=0; z<checkBoxes.size(); ++z) {
	    	checkBoxes.get(z).setEnabled(true);
	    }
    	
	    timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                keypress = new int[10];
                
                //printMoteReadings();
                
                for (int z=0; z<motes.size(); ++z) {
                	int moteReading;
                	
                	if (checkBoxes.get(z).isSelected()) {
                		moteReading = maxValue-random.nextInt(300);
                		readings.get(z).setText(String.valueOf(moteReading));
                	}
                	else {
                		moteReading = minValue+random.nextInt(300);
                		readings.get(z).setText(String.valueOf(moteReading));
                	}

                	motes.get(z).SetReading(moteReading);
                }
            }
        }, delay, period);
    }
    
	/**
	 * Adds a new mote to the collection if possible. Does not
	 * add a mote if one already exists with the same ID.
	 * @param moteId	The ID of the mote to be added
	 * @return	True if the mote is added successfully. False if there 
	 * 			already exists a mote with the same ID. 
	 */
	protected boolean addMote(int moteId){
		if (moteId < this.Count()) 
			return false;
		
		motes.add(new Mote(lblMotes.size()));
		addMoteRow();
		
		return true;
	}
    
	/**
	 * Returns the nth mote of the collection.
	 * @param index	The index of the mote to be returned
	 * @return		The mote specified by the index. If the index fails
	 * 				to specify a mote, returns null.
	 */
	public Mote getNthMote(int index){
		try {
			return motes.get(index);
		}catch (IndexOutOfBoundsException e){
			return null;
		}
	}
	
	/**
	 * Counts the amount of motes currently in the collection.
	 * @return 	The amount of motes in the current collection
	 */
	public int Count(){
		return motes.size();
	}	
	
	/**
	 * Returns the position of the mote with the specified Id. If
	 * the mote does not exist, it returns -1.
	 * @param	moteId	The requested moteId to find
	 * @return	The position of the mote if it is found or -1 if
	 * 			the mote is not found.
	 */
	public int MotePosition(int moteId)
	{
		for(int i = 0; i < Count(); ++i)
		{
			if(motes.get(i).GetId() == moteId)
			{
				return i;
			}
		}
		// 
		// if we've gotten this far, the mote was not found
		//
		return -1;
	}
	
	/**
	 * Finds the first mote with the given ID, if one exists, else 
	 * returns null.
	 * @param	moteId	The ID of the mote to find
	 * @return	The mote from the given ID if found, else returns null
	 */
	public Mote moteById(int moteId)
	{
		for(int i = 0; i < Count(); ++i)
		{
			Mote m = motes.get(i);
			if(m.GetId() == moteId)
			{
				return m;
			}
		}
		// 
		// if we've gotten this far, the mote was not found
		//
		return null;
		
	}
	
	/**
	 * Determines if the mote currently exists in the collection.
	 * @param moteId	The id of the mote
	 * @return			True if the mote is in the collection, else false
	 */
	public boolean MoteExists(int moteId)
	{
		return (moteById(moteId) != null);
	}
	
	/**
	 * This is useful for debugging.
	 */
	private void printMoteReadings() {
		for (int i=0; i<motes.size(); ++i) {
			//System.out.print(motes.get(i).Reading() + " ");
			System.out.print(getNthMote(i).Reading() + " ");
		}
		System.out.println("");
	}
    
	/**
	 * Timer will be disabled in manual mode.
	 */
    private void disableTimer() {
	    for (int z=0; z<checkBoxes.size(); ++z) {
	    	checkBoxes.get(z).setEnabled(false);
	    }
    	timer.cancel();
    	timerIsEnabled = false;
    }
}
